/* 一些业务相关的async-validator验证规则 */

// 空验证
export const isEmpt = ({ prompt, trigger = 'blur' } = {}) => [
  { required: true, message: prompt || `必填项`, trigger: trigger }
]

// 普通字符串长度 -  最小长度
export const minLength = ({ min, prompt, trigger = 'blur' } = {}) => [
  ...isEmpt({ trigger: trigger }),
  { min: min, message: prompt || `最少 ${min} 个字符`, trigger: trigger }
]

// 普通字符串长度 -  最大长度
export const maxLength = ({ max, prompt, trigger = 'blur' } = {}) => [
  ...isEmpt({ trigger: trigger }),
  { max: max, message: prompt || `最多 ${max} 个字符`, trigger: trigger }
]

// 普通字符串长度区间 -  最小 ~ 最大长度范围
export const rangeLength = ({ min, max, prompt, trigger = 'blur' } = {}) => [
  ...isEmpt({ trigger: trigger }),
  {
    max: max,
    message: prompt || `只允许 ${min} ~ ${max} 个字符`,
    trigger: trigger
  }
]

// 判断输入类型是Number
export const isNumber = ({ prompt, trigger = 'blur' } = {}) => [
  ...isEmpt({ trigger: trigger }),
  {
    validator: (rule, value, callback) => {
      if (isNaN(value))
        return callback(new Error(prompt || `只允许输入整数或小数`))
      callback()
    },
    trigger: trigger
  }
]

// Number(整形和浮点型)最小值
export const numMin = ({ min, prompt, trigger = 'blur' } = {}) => [
  ...isEmpt({ trigger: trigger }),
  {
    validator: (rule, value, callback) => {
      if (parseFloat(value) < parseFloat(min))
        return callback(new Error(prompt || `最小值为数字${min}`))
      callback()
    },
    trigger: trigger
  }
]

// Number(整形和浮点型)最大值
export const numMax = ({ max, prompt, trigger = 'blur' } = {}) => [
  {
    validator: (rule, value, callback) => {
      if (parseFloat(value) > parseFloat(max))
        return callback(new Error(prompt || `最大值为数字${max}`))
      callback()
    },
    trigger: trigger
  }
]

// Number(整形和浮点型) - 最小 ~ 最大长度范围
export const rangeNum = ({ min, max, prompt, trigger = 'blur' } = {}) => [
  ...isEmpt({ trigger: trigger }),
  {
    validator: (rule, value, callback) => {
      if (parseFloat(value) < parseFloat(min))
        return callback(
          new Error(prompt || `大小范围必须在 ${min} ~ ${max} 之间`)
        )
      if (parseFloat(value) > parseFloat(max))
        return callback(
          new Error(prompt || `大小范围必须在 ${min} ~ ${max} 之间`)
        )
      callback()
    },
    trigger: trigger
  }
]

// 正整数
export const integerNum = () => [
  {
    validator: (rule, value, callback) => {
      if (value && !/(^[1-9]\d*$)/.test(value)) {
        return callback(new Error(`只能输入正整数`))
      }
      callback()
    },
    trigger: 'blur'
  }
]

// 有效的百分比
export const percentage = (rule, value, callback) => {
  let str = String(value)
  let num = Number(value)
  if (value === '') {
    callback(new Error('必填项'))
  } else if (isNaN(num)) {
    callback(new Error('只能输入数字'))
  } else if (num < 0) {
    callback(new Error('不能小于0'))
  } else if (num > 100) {
    callback(new Error('不能大于100'))
  } else if (
    str.split('.') &&
    str.split('.')[1] &&
    str.split('.')[1].length > 2
  ) {
    callback(new Error('小数点后最多2位'))
  } else {
    callback()
  }
}

// 严格密码校验
export const strictModePwd = {
  validator: (rule, value, callback) => {
    if (value.includes(' ')) {
      return callback(new Error('密码不能包含空格'))
    }

    if (value.length > 20 || value.length < 12) {
      return callback(new Error('密码长度必须为12-20位字符'))
    }

    if (
      !/^(?=.*\d)(?=.*[a-zA-Z])(?=.*[!@#$%^&*()_\-=+~`[\]{}\|;:'"?/<>,.])[\da-zA-Z!@#$%^&*()_\-=+~`[\]{}\|;:'"?/<>,.]{3,}$/.test(
        value
      )
    ) {
      return callback(new Error('密码必须包含数字、字母、特殊字符'))
    }

    if (!/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{2,}$/.test(value)) {
      return callback(new Error('密码应至少包含一个大写字母和一个小写字母'))
    }

    if (value.match(/\d/g).length < 4) {
      return callback(new Error('密码应至少包含四个数字'))
    }

    let num = 0,
      values = value.split('')
    for (let i = 1; i < values.length; i++) {
      num = values[i] === values[i - 1] ? num + 1 : 0
      if (num >= 2) {
        return callback(
          new Error('不能存在连续3个或以上相同的字母或数字或特殊字符')
        )
      }
    }

    callback()
  },
  trigger: 'blur'
}

// 汉字，英文字符，数字，特殊符号
export const special = {
  validator: (rule, value, callback) => {
    if (
      value &&
      /[^\a-\z\A-\Z0-9\u4E00-\u9FA5\.\,\?\<\>\。\，\-\——\=\;\@\！\!\+\$]/g.test(
        value
      )
    ) {
      console.log(123)
      return callback(new Error(`只能输入汉字，英文字符，数字，特殊符号`))
    }
    callback()
  },
  trigger: 'blur'
}

export const required = {
  required: true,
  trigger: 'blur',
  //message: '必填项',
  validator: (rule, value, callback) => {
    //兼容非element场景调用
    if (
      typeof rule !== 'object' &&
      value === undefined &&
      callback === undefined
    ) {
      value = rule
    }

    let errInfo = ''
    if (isEmpty(value)) {
      errInfo = '必填项'
    }

    callback && callback(errInfo ? new Error(errInfo) : undefined)
    return errInfo
  }
}

export const coord = type => {
  if (!['lng', 'lat'].includes(type)) {
    throw Error('coord__方法的参数只能是lng或lat')
  }

  const validateRange = new Map([
    [
      'lng',
      value => {
        value = Number(value)
        if (value > 180 || value < -180) {
          return '经度的范围为[-180, 180]'
        }
      }
    ],
    [
      'lat',
      value => {
        value = Number(value)
        if (value > 90 || value < -90) {
          return '纬度的范围为[-90, 90]'
        }
      }
    ]
  ]).get(type)

  return {
    required: true,
    trigger: 'blur',
    validator: (rule, value, callback) => {
      //兼容非element场景调用
      if (
        typeof rule !== 'object' &&
        value === undefined &&
        callback === undefined
      ) {
        value = rule
      }

      let errInfo = ''
      if (isEmpty(value)) {
        errInfo = '必填项'
      } else if (/^-?\d+\.\d+$/.test(value)) {
        errInfo = validateRange(value)
        let decimalPart = String(value).split('.')[1]
        if (decimalPart.length > 6) {
          errInfo = '最多6位小数'
        }
      } else if (/^-?\d+$/.test(value)) {
        errInfo = validateRange(value)
      } else {
        errInfo = '格式不正确'
      }

      callback && callback(errInfo ? new Error(errInfo) : undefined)
      return errInfo
    }
  }
}

//手机
export const phone = {
  trigger: 'blur',
  validator: (rule, value, callback) => {
    //兼容非element场景调用
    if (
      typeof rule !== 'object' &&
      value === undefined &&
      callback === undefined
    ) {
      value = rule
    }

    let errInfo = ''
    if (!isEmpty(value) && !/^1\d{10}$/.test(value)) {
      errInfo = '格式不正确'
    }

    callback && callback(errInfo ? new Error(errInfo) : undefined)
    return errInfo
  }
}

//身份证
export const idCard = {
  trigger: 'blur',
  validator: (rule, value, callback) => {
    //兼容非element场景调用
    if (
      typeof rule !== 'object' &&
      value === undefined &&
      callback === undefined
    ) {
      value = rule
    }

    let errInfo = ''

    if (!isEmpty(value)) {
      // 加权因子
      let weight_factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
      // 校验码
      let check_code = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']

      let code = value + ''
      let last = value[17] //最后一位

      let seventeen = code.substring(0, 17)

      // ISO 7064:1983.MOD 11-2
      // 判断最后一位校验码是否正确
      let arr = seventeen.split('')
      let len = arr.length
      let num = 0
      for (let i = 0; i < len; i++) {
        num = num + arr[i] * weight_factor[i]
      }

      // 获取余数
      let resisue = num % 11
      let last_no = check_code[resisue]

      // 格式的正则
      // 正则思路
      /*
      第一位不可能是0
      第二位到第六位可以是0-9
      第七位到第十位是年份，所以七八位为19或者20
      十一位和十二位是月份，这两位是01-12之间的数值
      十三位和十四位是日期，是从01-31之间的数值
      十五，十六，十七都是数字0-9
      十八位可能是数字0-9，也可能是X
      */
      let idcard_patter = /^[1-9][0-9]{5}([1][9][0-9]{2}|[2][0][0|1][0-9])([0][1-9]|[1][0|1|2])([0][1-9]|[1|2][0-9]|[3][0|1])[0-9]{3}([0-9]|[X])$/

      // 判断格式是否正确
      let format = idcard_patter.test(value)

      // 返回验证结果，校验码和格式同时正确才算是合法的身份证号码
      if (!(last === last_no && format)) {
        errInfo = '格式不正确'
      }
    }

    callback && callback(errInfo ? new Error(errInfo) : undefined)
    return errInfo
  }
}

//座机
export const tel = (multiple = true) => {
  const regex = multiple ? /^[+\-;\d]*$/ : /^[+\-\d]*$/
  const length = multiple ? 50 : 20
  return {
    trigger: 'blur',
    validator: (rule, value, callback) => {
      //兼容非element场景调用
      if (
        typeof rule !== 'object' &&
        value === undefined &&
        callback === undefined
      ) {
        value = rule
      }

      let errInfo = ''
      if (!isEmpty(value)) {
        if (value.length > length) {
          errInfo = length + '个字符以内'
        } else if (!regex.test(value)) {
          errInfo =
            '格式不正确' + (multiple ? '，如有多个请用英文分号隔开' : '')
        }
      }

      callback && callback(errInfo ? new Error(errInfo) : undefined)
      return errInfo
    }
  }
}

export const email = {
  trigger: 'blur',
  validator: (rule, value, callback) => {
    //兼容非element场景调用
    if (
      typeof rule !== 'object' &&
      value === undefined &&
      callback === undefined
    ) {
      value = rule
    }

    let errInfo = ''
    if (
      !isEmpty(value) &&
      !/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/.test(value)
    ) {
      errInfo = '格式不正确'
    }

    callback && callback(errInfo ? new Error(errInfo) : undefined)
    return errInfo
  }
}

//账号
export const account = {
  trigger: 'blur',
  validator: (rule, value, callback) => {
    //兼容非element场景调用
    if (
      typeof rule !== 'object' &&
      value === undefined &&
      callback === undefined
    ) {
      value = rule
    }

    let errInfo = ''
    if (!/^[A-Za-z\d]+$/.test(value)) {
      errInfo = '需为数字或字母'
    }

    callback && callback(errInfo ? new Error(errInfo) : undefined)
    return errInfo
  }
}

export const len = (max, min) => {
  if (isNaN(max)) {
    throw Error('len__方法的第一个参数必填，且需为Number类型')
  }

  return {
    trigger: 'blur',
    validator: (rule, value, callback) => {
      //兼容非element场景调用
      if (
        typeof rule !== 'object' &&
        value === undefined &&
        callback === undefined
      ) {
        value = rule
      }

      let errInfo = ''
      if (!isEmpty(value)) {
        if (value.length > max) {
          errInfo = max + '个字符以内'
        } else if (min && value.length < min) {
          errInfo = min + '个字符以上'
        }
      }

      callback && callback(errInfo ? new Error(errInfo) : undefined)
      return errInfo
    }
  }
}

export const integer = ({
  required,
  len,
  maxLen,
  minLen,
  min,
  max,
  lessThan,
  greaterThan
} = {}) => {
  return {
    trigger: 'blur',
    required,
    validator: (rule, value, callback) => {
      //兼容非element场景调用
      if (
        typeof rule !== 'object' &&
        value === undefined &&
        callback === undefined
      ) {
        value = rule
      }

      let errInfo = ''
      if (required && isEmpty(value)) {
        errInfo = '必填项'
      } else if (value) {
        const numberValue = Number(value)
        const stringValue = String(value)
        switch (true) {
          case !/^-?\d*$/.test(value):
            errInfo = '需为整数'
            break
          case !isEmpty(min) && numberValue < min:
            errInfo = '不能小于' + min
            break
          case !isEmpty(max) && numberValue > max:
            errInfo = '不能大于' + max
            break
          case !isEmpty(greaterThan) && numberValue <= greaterThan:
            errInfo = '需大于' + greaterThan
            break
          case !isEmpty(lessThan) && numberValue >= lessThan:
            errInfo = '需小于' + lessThan
            break
          case !isEmpty(minLen) && stringValue.length < minLen:
            errInfo = minLen + '位以上'
            break
          case !isEmpty(maxLen) && stringValue.length > maxLen:
            errInfo = maxLen + '位以内'
            break
          case !isEmpty(len) && stringValue.length !== len:
            errInfo = '长度需为' + len + '位'
        }
      }

      callback && callback(errInfo ? new Error(errInfo) : undefined)
      return errInfo
    }
  }
}

export const decimal = ({
  required,
  allowInt = true,
  min,
  max,
  lessThan,
  greaterThan,
  decimalPlaces,
  minDecimalPlaces,
  maxDecimalPlaces
} = {}) => {
  return {
    trigger: 'blur',
    required,
    validator: (rule, value, callback) => {
      //兼容非element场景调用
      if (
        typeof rule !== 'object' &&
        value === undefined &&
        callback === undefined
      ) {
        value = rule
      }

      let errInfo = ''
      if (required && isEmpty(value)) {
        errInfo = '必填项'
      } else if (value) {
        const numberValue = Number(value)
        const stringValue = String(value)
        switch (true) {
          case !/(^\d+\.?\d+$)|(^\d+$)/.test(value):
            errInfo = '需为数字'
            break
          case !isEmpty(min) && numberValue < min:
            errInfo = '不能小于' + min
            break
          case !isEmpty(max) && numberValue > max:
            errInfo = '不能大于' + max
            break
          case !isEmpty(greaterThan) && numberValue <= greaterThan:
            errInfo = '需大于' + greaterThan
            break
          case !isEmpty(lessThan) && numberValue >= lessThan:
            errInfo = '需小于' + lessThan
            break
          default:
            const decimalPart = stringValue.split('.')
            if (!allowInt && isEmpty(decimalPart[1])) {
              errInfo = '需为小数'
            }
            if (!isEmpty(decimalPlaces)) {
              switch (true) {
                case decimalPlaces < 0:
                  throw Error('decimalPlaces不能小于0')
                case decimalPlaces === 0:
                  if (allowInt && !isEmpty(decimalPart[1])) {
                    errInfo = '需为整数'
                  }
                  break
                default:
                  if (decimalPart[1]?.length !== decimalPlaces) {
                    errInfo = '小数位数需为' + decimalPlaces + '位'
                  }
              }
            } else if (!isEmpty(maxDecimalPlaces)) {
              if (decimalPart[1]?.length > maxDecimalPlaces) {
                errInfo = '最多' + maxDecimalPlaces + '位小数'
              }
            } else if (!isEmpty(minDecimalPlaces)) {
              if (decimalPart[1]?.length < minDecimalPlaces) {
                errInfo = '最少' + maxDecimalPlaces + '位小数'
              }
            }
        }
      }

      callback && callback(errInfo ? new Error(errInfo) : undefined)
      return errInfo
    }
  }
}

export const pwd = {
  trigger: 'blur',
  validator: (rule, value, callback) => {
    //兼容非element场景调用
    if (
      typeof rule !== 'object' &&
      value === undefined &&
      callback === undefined
    ) {
      value = rule
    }

    let errInfo = ''
    if (!isEmpty(value)) {
      if (value.length < 8 || value.length > 16) {
        errInfo = '长度需为8~16位'
      } else if (
        !/^[a-zA-Z\d_()`·*&^%$#@!~{}|:"<>?\[\]\\\-;',\.\/]+$/.test(value)
      ) {
        errInfo = '需为字母/数字或常见的特殊符号'
      } else if (!/[a-zA-Z]/.test(value) || !/\d/.test(value)) {
        errInfo = '至少包含字母和数字'
      }
    }

    callback && callback(errInfo ? new Error(errInfo) : undefined)
    return errInfo
  }
}

export const verificationCode = {
  required: true,
  trigger: 'blur',
  validator: (rule, value, callback) => {
    //兼容非element场景调用
    if (
      typeof rule !== 'object' &&
      value === undefined &&
      callback === undefined
    ) {
      value = rule
    }

    let errInfo = ''
    if (isEmpty(value)) {
      errInfo = '必填项'
    } else if (value.length !== 5) {
      errInfo = '长度不正确'
    }

    callback && callback(errInfo ? new Error(errInfo) : undefined)
    return errInfo
  }
}

export const version = {
  trigger: 'blur',
  validator: (rule, value, callback) => {
    //兼容非element场景调用
    if (
      typeof rule !== 'object' &&
      value === undefined &&
      callback === undefined
    ) {
      value = rule
    }

    let errInfo = ''
    if (!isEmpty(value) && !/^\d{1,3}\.\d{1,3}\.\d{1,7}$/.test(value)) {
      errInfo = '格式不正确'
    }

    callback && callback(errInfo ? new Error(errInfo) : undefined)
    return errInfo
  }
}
export const password = {
  trigger: 'blur',
  validator: (rule, value, callback) => {
    let errInfo = ''
    if (value) {
      if (value.length < 12 || value.length > 30) {
        errInfo = '长度需为12~30位'
      } else if (
        !/^[a-zA-Z\d_()`·*&^%$#@!~{}|:"<>?\[\]\\\-;',\.\/]+$/.test(value)
      ) {
        errInfo = '需为字母/数字或常见的特殊符号'
      } else if (!/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{3,}$/.test(value)) {
        errInfo = '密码必须包含大小写字母和数字'
      } else if (errInfo == '') {
        let num = 0,
          num2 = 0,
          direction = 1
        for (let i = 1; i < value.length; i++) {
          // 是否有连续数字或字母
          let bad = value[i].charCodeAt() - value[i - 1].charCodeAt() // 差值
          if (direction == bad && (bad == 1 || bad == -1)) {
            num2 += 1 //存在连贯：计数+1
            direction = bad
          } else {
            num2 = 0 //不存在 0
          }

          if (num2 >= 2) {
            errInfo = '连续数字或连续字母不超过3位'
            break
          }

          // 是否有连续3个相同的数字或字母
          num = value[i] === value[i - 1] ? num + 1 : 0
          if (num >= 2) {
            errInfo = '不能存在连续3个或以上相同的字母或数字或特殊字符'
            break
          }
        }
      }
    }

    if (errInfo) {
      callback(new Error(errInfo))
    } else {
      callback()
    }
  }
}

export function isExternal (path) {
  return /^(https?:|mailto:|tel:)/.test(path)
}
